001    /*
002     * Copyright 2006 Stephen J. McConnell.
003     *
004     * Licensed  under the  Apache License,  Version 2.0  (the "License");
005     * you may not use  this file  except in  compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *   http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed  under the  License is distributed on an "AS IS" BASIS,
012     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
013     * implied.
014     *
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package net.dpml.util;
020    
021    import java.util.EventObject;
022    import java.util.List;
023    import java.util.LinkedList;
024    
025    import net.dpml.transit.monitor.LoggingAdapter;
026    
027    /**
028     * A abstract base class that established an event queue and handles event dispatch 
029     * operations for listeners declared in classes extending this base class.
030     *
031     * @author <a href="http://www.dpml.net">The Digital Product Meta Library</a>
032     * @version 1.0.0
033     */
034    public class EventQueue
035    {
036        // ------------------------------------------------------------------------
037        // state
038        // ------------------------------------------------------------------------
039        
040        private final EventDispatchThread m_thread;
041        
042        private final Logger m_logger;
043        
044        private final List m_queue;
045        
046        // ------------------------------------------------------------------------
047        // constructor
048        // ------------------------------------------------------------------------
049    
050       /**
051        * Creation of a new event queue.
052        * @param name the name used to construct a logging channel
053        */
054        public EventQueue( String name ) 
055        {
056            this( getLoggerForCategory( name ) );
057        }
058    
059       /**
060        * Creation of a new model.
061        * @param logger the assigned logging channel
062        * @exception NullPointerException if the supplied logging channel is null
063        */
064        public EventQueue( Logger logger ) 
065          throws NullPointerException
066        {
067            if( null == logger )
068            {
069                throw new NullPointerException( "logger" );
070            }
071            m_logger = logger;
072            m_queue = new LinkedList();
073            m_thread = new EventDispatchThread();
074            m_thread.setDaemon( true );
075            m_thread.start();
076        }
077    
078        // ------------------------------------------------------------------------
079        // EventQueue
080        // ------------------------------------------------------------------------
081    
082       /**
083        * Terminate the dispatch thread.
084        */
085        public synchronized void terminateDispatchThread()
086        {
087            if( null != m_thread )
088            {
089                m_thread.dispose();
090            }
091        }
092    
093       /**
094        * Return the assigned logging channel.
095        * @return the logging channel
096        */
097        private Logger getLogger()
098        {
099            return m_logger;
100        }
101        
102        /**
103         * A single background thread ("the event notification thread") monitors
104         * the event queue and delivers events that are placed on the queue.
105         */
106        private class EventDispatchThread extends Thread 
107        {
108            private boolean m_continue = true;
109    
110            void dispose()
111            {
112                synchronized( m_queue )
113                {
114                    m_continue = false;
115                    m_queue.notify();
116                }
117            }
118            
119            public void run() 
120            {
121                while( m_continue ) 
122                {
123                    // Wait on m_queue till an event is present
124                    EventObject event = null;
125                    synchronized( m_queue ) 
126                    {
127                        try 
128                        {
129                            while( m_continue && m_queue.isEmpty() )
130                            { 
131                                m_queue.wait();
132                            }
133                            if ( !m_continue )
134                            {
135                                break;
136                            }
137                            Object object = m_queue.remove( 0 );
138                            try
139                            {
140                                event = (EventObject) object;
141                            }
142                            catch( ClassCastException cce )
143                            {
144                                final String error = 
145                                  "Unexpected class cast exception while processing an event." 
146                                  + "\nEvent: " + object;
147                                throw new IllegalStateException( error );
148                            }
149                        }
150                        catch( InterruptedException e )
151                        {
152                            return;
153                        }
154                    }
155    
156                    Object source = event.getSource();
157                    if( source instanceof EventHandler )
158                    {
159                        EventHandler handler = (EventHandler) source;
160                        try
161                        {
162                            handler.processEvent( event );
163                        }
164                        catch( Throwable e )
165                        {
166                            final String error = 
167                              "Unexpected error while processing event."
168                              + "\nEvent: " + event
169                              + "\nSource: " + this;
170                            getLogger().error( error, e );
171                        }
172                    }
173                    else
174                    {
175                        final String error = 
176                          "Event source is not an instance of " 
177                          + EventHandler.class.getName();
178                        getLogger().error( error );
179                    }
180                }
181            }
182        }
183    
184        /**
185         * Enqueue an event for delivery to registered
186         * listeners unless there are no registered
187         * listeners.
188         *
189         * @param event the event object to add to the queue
190         */
191        public void enqueueEvent( EventObject event )
192        {
193            enqueueEvent( event, false );
194        }
195    
196        /**
197         * Enqueue an event for delivery to registered
198         * listeners unless there are no registered
199         * listeners.
200         *
201         * @param event the event object to add to the queue
202         * @param waitForCompletion if TRUE the implementation will apply
203         *   the event to the event source event handler and return on 
204         *   copmpletion of evetn delivery
205         */
206        public void enqueueEvent( EventObject event, boolean waitForCompletion )
207        {
208            if( !waitForCompletion )
209            {    
210                synchronized( m_queue ) 
211                {
212                    m_queue.add( event );
213                    m_queue.notify();
214                }
215            }
216            else
217            {
218                Object source = event.getSource();
219                if( source instanceof EventHandler )
220                {
221                    EventHandler handler = (EventHandler) source;
222                    try
223                    {
224                        handler.processEvent( event );
225                    }
226                    catch( Throwable e )
227                    {
228                        final String error = 
229                          "Unexpected error while processing event."
230                          + "\nEvent: " + event
231                          + "\nSource: " + source;
232                        getLogger().error( error );
233                    }
234                }
235                else
236                {
237                    final String error = 
238                      "Event source is not an instance of " 
239                      + EventHandler.class.getName()
240                      + "\nSource: " + source.getClass().getName();
241                    throw new IllegalStateException( error );
242                }
243            }
244        }
245    
246       /**
247        * Return a logging channel for the supplied name.
248        * @param name the name to use in construction of the logging channel
249        * @return the logging channel
250        */
251        static Logger getLoggerForCategory( String name )
252        {
253            if( null == name )
254            {
255                return new LoggingAdapter( "" );
256            }
257            else
258            {
259                return new LoggingAdapter( name );
260            }
261        }
262    }